<?php


/**
 * $o = new DB_Object;
 * $o->%X%Set(), $o->%X%Get() или $o->%X%(), $o->%X%Reset()
 * %Х%: fetchKey, fetchKeySeparator, fetchInto, fetchIntoctorargs, fetchIntocollection
 *
 */
class Engine_Db_Object
{
    protected
        $_con,
        $_cfg,
        $_cfghash,

/* IFP implement here and in __call() mthd */        
        $__fetchKey,
        $__fetchKeyDefault = array('id'),
        $__fetchKeySeparator,
        $__fetchKeySeparatorDefault = '/',

        $__fetchInto,
        $__fetchIntoDefault = 'DB_Data',
        $__fetchIntoCTORArgs,
        $__fetchIntoCTORArgsDefault,
        $__fetchIntoCollection,
        $__fetchIntoCollectionDefault = 'DB_Data_Collection'
    ;

	static $sqlCount = 0;

    public function __construct(array $cfg = array())
    {
        $this->_cfg = self::cfg($cfg);
        $this->_cfghash = md5(serialize($this->_cfg));
    }

    protected function &connection()
    {
        static $_;

        if (!$_)
        {
            $_ = array();
        }

        $key = $this->_cfghash;

        if (!array_key_exists($key, $_))
        {
            $_[$key] = new PDO
            (
                "{$this->_cfg['type']}:dbname={$this->_cfg['dbname']};host={$this->_cfg['host']};port={$this->_cfg['port']}",
                $this->_cfg['username'],
                $this->_cfg['password']
            );
        }

        return $_[$key];
    }

	public function getAll($sql, $params = array())
    {
		self::$sqlCount++;

		if (gettype($this->fetchIntoCollectionGet()) == 'object')
		{
			$oCollection = $this->fetchIntoCollectionGet();
		}
		else
		{
			$sCollection = $this->fetchIntoCollectionGet();
			$oCollection = new $sCollection();
		}

        $statement = $this->connection()->prepare($sql);

		if (gettype($this->fetchIntoGet()) == 'object')
		{
            $statement->setFetchMode(PDO::FETCH_INTO, $this->fetchIntoGet(), $this->fetchIntoCTORArgsGet());
		}
		else
		{
            $statement->setFetchMode(PDO::FETCH_CLASS, $this->fetchIntoGet(), $this->fetchIntoCTORArgsGet());
		}


        $statement->execute($params);

        $fetchKey = $this->fetchKeyParse();

        $i = 0;

        foreach ($statement AS $a)
        {
            $key = null;
            foreach ($fetchKey AS $fk)
            {
                if (isset($a->{$fk}))
                {
                    $key .= ($key ? $this->fetchKeySeparator() : '') . $a->{$fk};
                }
            }

			if (method_exists($a, 'setCollection'))
			{
				$a->setCollection($oCollection);
			}

            $oCollection->itemAppend($key ? $key : $i++, $a);
        }

        $this->reset();

        return $oCollection;
    }

    public function getRow($sql, $params = array())
    {
        $statement = $this->connection()->prepare($sql);

		if (gettype($this->fetchIntoGet()) == 'object')
		{
            $statement->setFetchMode(PDO::FETCH_INTO, $this->fetchIntoGet());
		}
		else
		{
            $statement->setFetchMode(PDO::FETCH_CLASS, $this->fetchIntoGet(), $this->fetchIntoCTORArgsGet());
		}

        $statement->execute($params);

        $this->reset();

        foreach ($statement AS $a)
        {
            return $a;
        }
    }

    public function getOne($sql, $params = array())
    {
        $statement = $this->connection()->prepare($sql);
        $statement->execute($params);

        $this->reset();
        
        return $statement->fetchColumn();
    }

    public function insert($sql, $params = array())
    {
        $statement = $this->connection()->prepare($sql);
        $statement->execute($params);

        $this->reset();

        return $this->lastInsertId();
    }

    public function update($sql, $params = array())
    {
        $statement = $this->connection()->prepare($sql);
        $statement->execute($params);
        $this->reset();
    }

    public function lastInsertId()
    {
        return $this->connection()->lastInsertId();
    }

    public function reset()
    {
        $this->fetchKeyReset();
        $this->fetchKeySeparatorReset();
        $this->fetchIntoReset();
        $this->fetchIntoCollectionReset();
        $this->fetchIntoCTORArgsDefaultReset();
    }

    public function &__call($mthd, $args)
    {
        $possible = array('fetchKeySeparator', 'fetchKey', 'fetchIntoCollectionDefault', 'fetchIntoCollection', 'fetchIntoCTORArgsDefault', 'fetchIntoCTORArgs', 'fetchIntoDefault', 'fetchInto');
        foreach ($possible AS $v)
        {
            if (strpos($mthd, $v) === 0)
            {
                $t = $v;
                break;
            }
        }
        
        if (empty($t))
        {
            throw new DB_Object_Exception("Method '$mthd' doesn't exist in " . get_class($this));
        }
        
        switch ($mthd = strtolower(str_replace($t, '', $mthd)))
        {
            case 'set':
                $this->{"__{$t}"} = !empty($args[0]) ? $args[0] : null;
                return $this;
            break;

            case 'reset':
                $this->{"__{$t}"} = null;
                return $this;
            break;

            default:
            case 'get':
                if (!$this->{"__{$t}"})
                {
                    return $this->{"__{$t}Default"};
                }
                return $this->{"__{$t}"};
            break;
        }

        if (empty($t))
        {
            throw new DB_Object_Exception("Method '$mthd' doesn't exist in " . get_class($this));
        }
    }

    protected function fetchKeyParse()
    {
        $fk = $this->fetchKeyGet();
        if (is_string($fk))
        {
            return explode('/', $fk);
        }
        if (!is_array($fk))
        {
            return array($fk);
        }
        return $fk;
    }

	protected static function cfg($cfg = array())
	{
		if (!$cfg)
		{
		    $cfg = Zend_Registry::get('config')->db->params->toArray();
		}
		if (!@$cfg['type'])
		{
		    $cfg['type'] = 'mysql';
		}
		if (!@$cfg['port'])
		{
		    $cfg['port'] = 3306;
		}
	    return $cfg;
	}
}


class DB_Object_Exception extends Exception
{
    public function __construct($m = null, $c = 0)
    {
        parent::__construct($m, $c);
    }
}